#include <iostream>
#include "clips.h"
#include <client/dbclient.h>
/** \var extern std::list<std::string> rmatch
    \brief Extern variable allowing access to the same declared in ruleEngine.c. It stores the ID of the matched rules
    */
extern std::list<std::string> rmatch;
/** \fn void alert()
    \brief Allows the rules to specify a string with which they want to be identified
    * Usage: (alert "String-value")
    * Postcondition: Saves the ID in a list which stores the IDs of matched rules
    * Notes: All arguments and return values are handled by CLIPS functions
    \param void
    \return void */
void alert()
{
    using namespace std;
    /*CLIPS object for handling arguments*/
    DATA_OBJECT temp;
    /*Check the number of arguments*/
    int args=RtnArgCount();
    if(args!=1)
    {
        return;
    }
    /*Check if the argument passed is a symbol or string*/
    if(ArgTypeCheck("alert",1,SYMBOL_OR_STRING,&temp)==0)
    {
        return;
    }
    /*Retrieve the argument at 1 position*/
    string rname=RtnLexeme(1);
    /*Save the argument in the list of matched arguments*/
    rmatch.push_back(rname);
}
/*!-----------------------------------DEPRECATED FUNCTION---------------------------!*/
int rdbcount()
{
    using namespace std;
    using namespace mongo;
    DATA_OBJECT temp;
    string dbaddr,collection,bjson,oid,keyoid;
    int args=RtnArgCount();
    if(args!=5)
    {
        return(-1);
    }
    dbaddr=RtnLexeme(1);
    collection=RtnLexeme(2);
    bjson=RtnLexeme(3);
    keyoid=RtnLexeme(4);
    oid=RtnLexeme(5);
    DBClientConnection c;
    BSONObj bj,q;
    BSONObjBuilder x;
    try
    {
        bj=fromjson(bjson);
        x.appendElements(bj);
        x.append(keyoid,OID(oid));
        q=x.obj();
        c.connect(dbaddr);
    }
    catch(DBException e)
    {
        return(-1);
    }
    auto_ptr<DBClientCursor> cursor;
    try
    {
        cursor=c.query(collection,q);
    }
    catch(DBException e)
    {
        return(-1);
    }
    int count=cursor->itcount();
    return(count);
}
/** \fn void *dbquery()
    \brief Query the database and returns a pointer to a BSON Object
    * Usage: (dbquery "dbaddress" "collectionName" "QueryString")
    * Note 1: Returns a pointer to a BSON Object which has to be binded to some variable so that it can be used multple
    * times
    * Note 2: Use functions findString() and findInt() to retrieve values
    * Note 3: Always use destructQuery() to deallocate memory for the returned object after its purpose is completed
    * Note 4: In case of multiple results the first object will be returned which varies with mongoDB
    /param void
    /return void * Pointer to BSON Object */
void * dbquery()
{
    using namespace std;
    using namespace mongo;
    DATA_OBJECT temp;
    void *returnvalue;
    string dbaddr,collection,q,ret;
    BSONObj *x;
    int args=RtnArgCount();
    if(args!=3)
    {
        return(NULL);
    }

    dbaddr=RtnLexeme(1);
    collection=RtnLexeme(2);
    DBClientConnection c;
    try
    {
        c.connect(dbaddr);
    }
    catch(DBException e)
    {
        return(NULL);
    }
    q=RtnLexeme(3);
    /*Query the database*/
    auto_ptr<DBClientCursor> cursor;
    try
    {
        cursor=c.query(collection,Query(q),1);
    }
    catch(DBException e)
    {
        return(NULL);
    }
    if(cursor->more())
    {
        x=new BSONObj(cursor->next().getOwned());
        std::cout<<x->toString()<<endl;
        return((void *)x);
    }
    else
    {
        return(NULL);
    }
}
/** \fn void destructQuery()
    \brief Deallocate memory for the BSON Object reference
    * Usage (destructQuery ?pointer-to-BSON)
    *Note 1: Used after dbQuery()
    \param void
    \return void */
void destructQuery()
{
    using namespace mongo;
    int args=RtnArgCount();
    DATA_OBJECT temp;
    if(args!=1)
    {
        return;
    }
    /*Fetch the 1 argument as unknown type*/
    RtnUnknown(1,&temp);
    /*Cast it to a void pointer*/
    void * x=DOToPointer(temp);
    if(x==NULL)
    {
        return;
    }
    /*Cast it to a BSONObj pointer*/
    BSONObj *b=(BSONObj *)x;
    /*Deallocate the memory*/
    delete b;
    /*Set NULL for future references*/
    x=NULL;
}
/** \fn int findInt()
    \brief Return the integer value corresponding to the key from the passed BSONObj pointer
    * Usage: (findInt ?pointer-to-BSONObj ?key)
    * Note 1: It is expected that return value should be binded to a variable
    \param void
    \return int The integer value for key */
int findInt()
{
    using namespace mongo;
    if(ArgCountCheck("findString",EXACTLY,2)==-1)
    {
        return(-1);
    }
    DATA_OBJECT temp;
    RtnUnknown(1,&temp);
    BSONObj *b=(BSONObj *)DOToPointer(temp);
    string key=RtnLexeme(2);
    BSONElement v=b->getFieldDotted(key);
    /*Check if the value is an integer type*/
    if(v.type()==NumberInt)
    {
        return(v.numberInt());
    }
    return(-1);
}
/** \fn void * findString()
    \brief Find the string value corresponding to a key int the passed BSONObj pointer
    * Usage: (findString ?pointer-to-BSONObj ?key-value)
    * Note 1: Same as findInt()
    \param void
    \return void * String value */
void * findString()
{
    using namespace mongo;
    if(ArgCountCheck("findString",EXACTLY,2)==-1)
    {
        return(AddSymbol(""));
    }
    DATA_OBJECT temp;
    RtnUnknown(1,&temp);
    BSONObj *b=(BSONObj *)DOToPointer(temp);
    string key=RtnLexeme(2);
    BSONElement v=b->getFieldDotted(key);
    /* Because of C-C++ incompatibility issues*/ 
    char buff[1024];
    strcpy(buff,v.str().c_str());
    if(v.type()==String)
    {
       /*Add a string return value in the CLIPS tables*/
        return(AddSymbol(buff));

    }

    return(AddSymbol("#Invalid type"));
    
}
/** \fn int dbcount()
    \brief Return the count of objects found given a string query
    * Usage: (dbcount "db-address" "collection" "query")
    \param void
    \return int
    */
int dbcount()
{
    using namespace std;
    using namespace mongo;
    DATA_OBJECT temp;
    string dbaddr,collection;
    int args=RtnArgCount();
    if(args!=3) 
    {
        return(-1);
    }
    dbaddr=RtnLexeme(1);
    collection=RtnLexeme(2);
    DBClientConnection c;
    try
    {
        c.connect(dbaddr);
    }catch(DBException e)
    {
        return(-1);
    }
    string bjson=RtnLexeme(3);
    
    auto_ptr<DBClientCursor> cursor;
    try{
        cursor=c.query(collection,Query(bjson));
    }
    catch(DBException e)
    {
        return(-1);
    }
    int count=cursor->itcount();
    return(count);
}
